package utils.page;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;


import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.Pattern;


import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.CaseFormat;
import com.google.common.collect.Ordering;
import com.google.gson.Gson;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReflectUtil;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import utils.ValidUtils;
import utils.page.PageUtil.PageParam.Sort;

/**
 * @author jiangfeng on 2022/6/14
 */
public class PageUtil {
    static Gson gson = new Gson();

    // Page 对象来源于: com.baomidou:mybatis-plus-extension:3.5.2
    public static <V> Page<V> prodPage(PageParam pageParam, Class<V> clazz) {
        ValidUtils.validateObj(pageParam);
        Page<V> page = new Page<>(pageParam.getPageNum(), pageParam.getPageSize());
        List<PageParam.Sort> sorts = pageParam.getSorts();
        for (PageParam.Sort sort : sorts) {
            validField(clazz, sort.getField());
            OrderItem orderItem = sort.isAsc() ? OrderItem.asc(sort.getField()) : OrderItem.desc(sort.getField());
            page.addOrder(orderItem);
        }
        return page;
    }

    public static <T> PageDTO<T> pageOf(Page<T> page) {
        PageDTO<T> pageDTO = new PageDTO<>();
        pageDTO.setList(page.getRecords());
        pageDTO.setPageNum(page.getCurrent());
        pageDTO.setPageSize(page.getSize());
        pageDTO.setTotal(page.getTotal());
        return pageDTO;
    }

    public static <T> PageDTO<T> genEmptyPage(PageParam pageParam) {
        PageDTO<T> pageDTO = new PageDTO<>();
        pageDTO.setPageNum(pageParam.getPageNum());
        pageDTO.setPageSize(pageParam.getPageSize());
        return pageDTO;
    }

    /**
     * 内存分页,支持排序
     *
     * @param page 实际数据
     * @param pageParam 分页参数,带排序.
     */
    public static <T> PageDTO<T> memPageAndSort(List<T> page, PageParam pageParam) {
        Assert.notNull(page);
        PageDTO<T> pageDTO = new PageDTO<>();
        pageDTO.setTotal(page.size());
        List<Sort> sorts = pageParam.getSorts();

        // 默认暂时只支持单字段排序
        if (!CollectionUtils.isEmpty(sorts)) {
            Sort sort = sorts.get(0);
            String field = sort.getField();
            Ordering<Comparable> natural = Ordering.natural();
            Comparator<T> comparing =
                    (c1, c2) -> natural.compare((Comparable) getValue(c1, field), (Comparable) getValue(c2, field));
            if (!sort.isAsc()) {
                comparing = comparing.reversed();
            }
            page.sort(comparing);
        }
        page = page.stream()
                .skip((pageParam.getPageNum() - 1) * pageParam.getPageSize())
                .limit(pageParam.getPageSize())
                .collect(Collectors.toList());
        pageDTO.setList(page);
        pageDTO.setPageNum(pageParam.getPageNum());
        pageDTO.setPageSize(pageParam.getPageSize());
        return pageDTO;
    }

    public static <T> Object getValue(T e, String fieldName) {
        return gson.fromJson(gson.toJson(e), HashMap.class).get(fieldName);
    }


    /**
     * 内存分页
     *
     * @param page 实际数据
     * @param pageParam 分页参数
     */
    public static <T> PageDTO<T> memPage(List<T> page, PageParam pageParam) {
        Assert.notNull(page);
        PageDTO<T> pageDTO = new PageDTO<>();
        pageDTO.setTotal(page.size());
        page = page.stream()
                .skip((pageParam.getPageNum() - 1) * pageParam.getPageSize())
                .limit(pageParam.getPageSize())
                .collect(Collectors.toList());
        pageDTO.setList(page);
        pageDTO.setPageNum(pageParam.getPageNum());
        pageDTO.setPageSize(pageParam.getPageSize());
        return pageDTO;
    }


    public static <T, E> PageDTO<E> pageOf(Page<T> page, Function<T, E> mapper) {
        PageDTO<E> pageDTO = new PageDTO<>();
        pageDTO.setList(page.getRecords().stream().map(mapper).collect(Collectors.toList()));
        pageDTO.setPageNum(page.getCurrent());
        pageDTO.setPageSize(page.getSize());
        pageDTO.setTotal(page.getTotal());
        return pageDTO;
    }

    public static <T> void validField(Class<T> clazz, String column) {
        if (!validFieldExist(clazz, column)) {
            //throw new ValidationException("排序字段不合法");
            throw new RuntimeException("排序字段不合法");
        }
    }

    public static <T> boolean validFieldExist(Class<T> clazz, String column) {
        return ReflectUtil.getField(clazz, CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, column)) != null;
    }


    // implementation 'io.swagger:swagger-annotations:1.6.7'
    @ApiModel(value = "Page", description = "分页对象")
    public static class PageDTO<T> {

        @ApiModelProperty(value = "页码", required = true)
        private long pageNum;
        @ApiModelProperty(value = "每页数量", required = true)
        private long pageSize;
        @ApiModelProperty(value = "总数量", required = true)
        private long total;
        @ApiModelProperty(value = "数据对象集合", required = true)
        private List<T> list;

        public long getPageNum() {
            return pageNum;
        }

        public void setPageNum(long pageNum) {
            this.pageNum = pageNum;
        }

        public long getPageSize() {
            return pageSize;
        }

        public void setPageSize(long pageSize) {
            this.pageSize = pageSize;
        }

        public long getTotal() {
            return total;
        }

        public void setTotal(long total) {
            this.total = total;
        }

        public List<T> getList() {
            return list;
        }

        public void setList(List<T> list) {
            this.list = list;
        }

        public static <T, E> PageDTO<T> pageOf(PageDTO<E> page, Function<E, T> mapper) {
            PageDTO<T> newPage = new PageDTO<>();
            newPage.setList(page.getList().stream().map(mapper).collect(Collectors.toList()));
            newPage.setPageNum(page.getPageNum());
            newPage.setPageSize(page.getPageSize());
            newPage.setTotal(page.getTotal());
            return newPage;
        }

        public static <R> PageDTO<R> pageOf(Integer pageNum, Integer pageSize, List<R> data) {
            PageDTO<R> newPage = new PageDTO<>();
            newPage.setList(data);
            newPage.setPageNum(pageNum);
            newPage.setPageSize(pageSize);
            return newPage;
        }

        public static <T> PageDTO<T> empty() {
            PageDTO<T> pageDTO = new PageDTO<>();
            pageDTO.setPageNum(0);
            pageDTO.setPageSize(0);
            pageDTO.setTotal(0);
            pageDTO.setList(Collections.emptyList());
            return pageDTO;
        }

        public static <T> PageDTO<T> empty(long pageNum, long pageSize) {
            PageDTO<T> pageDTO = new PageDTO<>();
            pageDTO.setPageNum(pageNum);
            pageDTO.setPageSize(pageSize);
            pageDTO.setTotal(0);
            pageDTO.setList(Collections.emptyList());
            return pageDTO;
        }

        public long getPages() {
            if (pageSize == 0L) {
                return 0L;
            } else {
                long pages = total / pageSize;
                if (total % pageSize != 0L) {
                    ++pages;
                }
                return pages;
            }
        }
    }


    @Data
    public static class PageParam {

        // javax.validation:validation-api:2.0.1.Final
        @Min(value = 1, message = "当前页不能小于 1")
        private long pageNum = 1;

        @Min(value = 1, message = "每页显示条数不能小于1")
        private long pageSize = 10;

        @Valid
        private List<Sort> sorts = new ArrayList<>();

        public long getPageNum() {
            return pageNum;
        }

        public long getPageSize() {
            return pageSize;
        }

        public List<Sort> getSorts() {
            return sorts;
        }

        @Getter
        @Setter
        public static class Sort {

            @Pattern(regexp = PageParamRequest.SORT_FILED_REGEX, message = "排序字段格式非法")
            private String field;

            private boolean asc;

            public String getField() {
                return field;
            }

            public boolean isAsc() {
                return asc;
            }
        }

    }

    /**
     * 前端交互请求入参模型，将被转换为 PageParam 对象
     *
     * @author jiangfeng 2022-06-14
     * @see PageParam
     */
    @Data
    @Valid
    public static class PageParamRequest {

        public static final String SORT_FILED_REGEX = "(([A-Za-z0-9_]{1,10}.)?[A-Za-z0-9_]{1,64})";

        public static final String SORT_FILED_ORDER = "(desc|asc)";

        public static final String SORT_REGEX = "^" + SORT_FILED_REGEX + "(," + SORT_FILED_ORDER + ")*$";

        @Min(value = 1, message = "当前页不能小于 1")
        private long current = 1;

        @Min(value = 1, message = "每页显示条数不能小于1")
        private long size = 10;


        /**
         * 排序规则，当此属性有值时，忽略 sortFields 和 sortOrders
         */
        @javax.validation.constraints.Pattern(regexp = SORT_REGEX, message = "排序字段格式非法")
        private List<String> sort;

    }
}
